package cn.wolfcode.shiro;

import cn.wolfcode.domain.Employee;
import cn.wolfcode.domain.Permission;
import cn.wolfcode.domain.Role;
import cn.wolfcode.mapper.PermissionMapper;
import cn.wolfcode.service.IEmployeeService;
import cn.wolfcode.service.IPermissionService;
import cn.wolfcode.service.IRoleService;
import cn.wolfcode.util.UserContext;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * @Description: 自定义数据源
 * @Author: 伍家钰
 * @Date: 2020/7/18 20:21
 * @Version: 1.0
 */
// 继承与授权管理器
@Component
public class CrmRealm extends AuthorizingRealm {
    @Autowired
    private IEmployeeService employeeServiceImpl;
    @Autowired
    private IRoleService iRoleServiceImpl;
    @Autowired
    private PermissionMapper permissionMapper;

    /**
     * 提供授权信息
     *
     * @param principalCollection
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("===========================================");
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
       /* // 1.获取当前登入员工的对象,获取当前员工id
        // 2.查询数据库该员工拥有的角色和权限数据
        // 3.给用户分配角色
        info.addRoles(Arrays.asList("hr", "seller"));
        // 4.给角色添加权限
        info.addStringPermissions(Arrays.asList("employee:save", "employee:delete"));
        return info;*/
        //---------------------------------------------------------------------------
        // 1.获取当前登入的主体对象
        //Subject subject = SecurityUtils.getSubject();
        // 2.获取主体对象的身份信息(2种方式)
        Employee employee = UserContext.getEmployee();
        // Employee employee = (Employee) principalCollection.getPrimaryPrincipal();
        // 3.判断用户是否是管理员
        if (!employee.isAdmin()) {
            // 不是管理员则,根据id查询当前登入用户拥有的角色的编码
            List<String> snss = iRoleServiceImpl.selectSnsByEmployeeId(employee.getId());
            ArrayList<String> roleSnList = new ArrayList<>();
            for (String sns : snss) {
                // 将当前登入用户拥有的角色全部放入集合中
                roleSnList.add(sns);
            }
            // 将用户拥有的角色添加到info对象中,放回给调用者
            info.addRoles(roleSnList);
            // 根据id查询用户拥有的权限
            List<String> expressions = permissionMapper.selectPermissionByEmployeeId(employee.getId());
            // 将员工包含的角色的所有权限添加给返回的info对象中
            info.addStringPermissions(expressions);
        } else {
            // 是管理员则给其添加一个管理员角色,方便后面使用subject来判断是否是管理员
            info.addRole("admin");
            // 给角色添加所有权限(权限通配符)
            info.addStringPermission("*:*");
        }
        return info;
    }

    /**
     * 提供认证信息
     *
     * @param token
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        // 获取令牌中传入过来的用户名
        String username = (String) token.getPrincipal();
        // 从数据库中查询账号信息
        Employee employee = employeeServiceImpl.selectByName(username);
        // 如果账号存在则返回一个AuthorizationInfo对象
        if (employee != null) {
            // employee身份信息可以在任意地方获取到,与subject绑在一起
            // 传入真实的密码,shiro会自动从返回对象获取密码,再与token去比较
            // this.getName()返回当前数据源的名称,一般是一个项目中有多个数据源时起作用,代表数据是从哪个数据源中查的
            // 因为AuthenticationInfo是接口所以用其实现类SimpleAuthenticationInfo
            return new SimpleAuthenticationInfo(employee, employee.getPassword(), this.getName());
        }
        // 如果不存在则放回null,此时shiro会自动抛出账号不存在的异常
        return null;
    }
}
